﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Navigant.Infrastructure;
using Navigant.Infrastructure.Cases;
using Navigant.Infrastructure.Helpers;
using Navigant.Model.Custodians;
using Navigant.Model.PreservationNotices;

namespace Navigant.Application
{
    public static class PreservationNoticeProcessingService
    {
        public static void ProcessBatchNotices(ICase currentCase)
        {
            // Get a unit of work context
            IUnitOfWork unitOfWork = UnitOfWork.New();

            PreservationNoticeBatchService service =
                new PreservationNoticeBatchService(currentCase,unitOfWork);
            PreservationNoticeBatch batch = service.GetNextBatch();

            Dictionary<PreservationNoticeBatch, string> sentBatches = new Dictionary<PreservationNoticeBatch, string>();
            List<PreservationNoticeBatch> attemptedBatches = 
                new List<PreservationNoticeBatch>();
            Dictionary<PreservationNoticeBatch, string> unsentBatches = new Dictionary<PreservationNoticeBatch, string>();

            while (batch != null)
            {
                // Log the start of the process
                Logger.LogMessage(string.Format("Started the batch send process for the {0} case site.", 
                    currentCase.Name));

                //Initialize
                IList<Custodian> custodiansWithSendError = null;
                attemptedBatches.Add(batch);

                // Notify the sender (fromAddress) that processing has started
                PreservationNoticeProcessingService.SendBatchProcessingNotificationToSender(batch);

                // Try to send the batch
                try
                {
                    // Get the list of custodians
                    IList<Custodian> custodians = null;
                    CustodianService custodianService = new CustodianService(currentCase, unitOfWork);

                    if (batch.ExtraCriteriaName.Length > 0 || batch.ExtraCriteriaValue.Length > 0)
                    {
                        custodians = custodianService.GetCustodiansBy(batch.ExtraCriteriaName, 
                            batch.ExtraCriteriaValue,false);
                    }
                    else
                    {
                        custodians = custodianService.GetCustodiansBy(batch.Criteria);
                    }

                    // Send the notice to the custodians
                    custodiansWithSendError = PreservationNoticeSendingService.SendNotices(currentCase, 
                        custodians, batch.NoticeId, batch.FromEmailAddress);

                    // Log the results
                    sentBatches.Add(batch, string.Empty);
                }
                catch (Exception e)
                {
                    // Log the error
                    Logger.LogError(e, currentCase);
                    Logger.LogMessage(string.Format("Error sending the following Preservation Notice queued item:  {0}.  Details:  {1}", 
                        batch.Key, e), currentCase);
                    unsentBatches.Add(batch, e.ToString());
                }
                finally
                {
                    try
                    {
                        // Email the results of the batch to the sender (fromAddress)
                        PreservationNoticeProcessingService.SendBatchResultsToSender(batch,
                            custodiansWithSendError);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError(ex, currentCase);
                    }
                }

                // Get the next batch to process
                batch = service.GetNextBatch();
            }

            // Log the results of the batch processing and clear the queue
            PreservationNoticeProcessingService.UpdateBatchQueue(currentCase, 
                attemptedBatches, sentBatches, unsentBatches, unitOfWork);

            // Commit the batch processing results as one Unit of Work
            unitOfWork.Commit();

            // Log the completion of the process
            if (sentBatches.Count > 0)
            {
                StringBuilder builder = new StringBuilder();
                builder.Append(string.Format("Finished batch sending for case site {0}.  The following queued items were processed: ", 
                    currentCase.Name));
                foreach (KeyValuePair<PreservationNoticeBatch, string> keyPair in sentBatches)
                {
                    builder.Append(keyPair.Key.Id.ToString());
                    builder.Append(" ");
                }
                Logger.LogMessage(builder.ToString(), currentCase);
            }
        }

        private static void SendBatchProcessingNotificationToSender(PreservationNoticeBatch batch)
        {
            string subject = string.Format("The send processing for Preservation Notice ID {0} has started",
                batch.NoticeId);
            StringBuilder body = new StringBuilder(300);
            body.Append(subject);
            body.Append("\n\r \n\r");
            body.Append("You will receive another notification email when the processing has completed, and that email will detail the results of the processing.");
            EmailHelper.SendMessage(batch.FromEmailAddress,
                batch.FromEmailAddress, subject, body.ToString());
        }

        private static void SendBatchResultsToSender(PreservationNoticeBatch batch,
            IList<Custodian> custodiansWithSendError)
        {
            StringBuilder body = new StringBuilder(200);
            string subject = string.Empty;

            if (custodiansWithSendError != null && custodiansWithSendError.Count > 0)
            {
                subject = string.Format("Processing has completed.  There was an error sending NoticeID {0}",
                    batch.NoticeId);
                body.Append(string.Format("Processing has completed.  There was an error sending NoticeID {0} to the following custodians:",
                    batch.NoticeId));
                body.Append("\n\r \n\r");
                foreach (Custodian custodian in custodiansWithSendError)
                {
                    body.Append(string.Format("Custodian Name:  {0}  ID:  {1}",
                        custodian.Name, custodian.Id));
                    body.Append("\n\r");
                }
            }
            else
            {
                subject = string.Format("The processing has completed for NoticeID {0} with no errors",
                    batch.NoticeId);
                body.Append(string.Format("NoticeID {0} was successfully sent with no errors.",
                    batch.NoticeId));                
            }

            body.Append("\n\r \n\r");
            body.Append("For more information please see the SendNoticeHistory table");

            EmailHelper.SendMessage(batch.FromEmailAddress,
                batch.FromEmailAddress,
                subject,
                body.ToString());
        }

        private static void UpdateBatchQueue(ICase currentCase, 
            IList<PreservationNoticeBatch> attemptedBatches,
            Dictionary<PreservationNoticeBatch, string> successfulBatches, 
            Dictionary<PreservationNoticeBatch, string> failedBatches,
            IUnitOfWork unitOfWork)
        {
            PreservationNoticeBatchService service =
                new PreservationNoticeBatchService(currentCase, unitOfWork);
            
            try
            {
                // Log the failures
                foreach (PreservationNoticeBatch key in failedBatches.Keys)
                {
                    service.LogBatchResult(key,
                        PreservationNoticeBatchProcessStatus.Failed,
                        failedBatches[key]);
                }
                // Log the successes
                foreach (PreservationNoticeBatch key in successfulBatches.Keys)
                {
                    service.LogBatchResult(key,
                        PreservationNoticeBatchProcessStatus.Processed,
                        string.Empty);
                }
                // Clear the queue
                List<PreservationNoticeBatch> batchesToRemove = new List<PreservationNoticeBatch>();
                foreach (PreservationNoticeBatch key in successfulBatches.Keys)
                {
                    batchesToRemove.Add(key);
                }
                service.RemoveBatches(batchesToRemove);
            }
            catch (Exception e)
            {
                Logger.LogError(e, currentCase);
            }
        }
    }
}
